package com.example.aidlclient

import aidlserver.app.IAidlCallback
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.widget.Toast
import com.example.aidlclient.databinding.ActivityMainBinding
import aidlserver.app.IBook
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.os.DeadObjectException
import android.os.ParcelFileDescriptor
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.example.commonlib.CLogUtil
import kotlinx.coroutines.*
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.util.concurrent.atomic.AtomicBoolean

/**
 * aidl 客户端
 */
class ClientMainActivity : AppCompatActivity() {

    private var serviceConnection: ServiceConnection? = null
    private var iBookInterface: IBook? = null

    // 发送文件
    private val isInSendFile = AtomicBoolean(false)
    private lateinit var viewBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        CLogUtil.d("onCreate")

        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)
        initClicks()

        lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                super.onResume(owner)
                connectRemoteService()
                owner.lifecycle.removeObserver(this)
            }
        })
    }

    private fun obtainServiceConnection(): ServiceConnection {
        return object : ServiceConnection {
            override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
                serviceConnection = this
                CLogUtil.d("xxxx onServiceConnected p0 = [${p0}], p1 = [${p1}]")
                val iBook: IBook = IBook.Stub.asInterface(p1)
                iBookInterface = iBook
            }

            override fun onServiceDisconnected(p0: ComponentName?) {
                CLogUtil.d("xxxx onServiceDisconnected p0 = [${p0}]")
            }
        }
    }

    @SuppressLint("SetTextI18n")
    private fun connectRemoteService() {
        val remoteServiceIntent = getRemoteServiceIntent()
        var ret = false
        var msg = ""
        try {
            ret = this.applicationContext.bindService(remoteServiceIntent, obtainServiceConnection(), Service.BIND_AUTO_CREATE)
            if (!ret) {
                viewBinding.infoTv.text = "connect remote service error!"
            } else {
                viewBinding.infoTv.text = "connect remote service success!"
            }
        } catch (ex: SecurityException) {
            ex.printStackTrace()
            msg = ex.message ?: ""
        }
        CLogUtil.d("bind ${remoteServiceIntent.action} ret $ret $msg")
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        iBookInterface = null
        isInSendFile.compareAndSet(true, false)
    }

    @SuppressLint("SetTextI18n")
    private fun initClicks() {
        viewBinding.test.setOnClickListener {
            if (iBookInterface == null) {
                Toast.makeText(it.context, "未获取到binder对象", Toast.LENGTH_SHORT).show()
            } else {
                iBookInterface?.borrowBook("you are best!", 202288)
            }
        }


        viewBinding.fileTest.setOnClickListener {
            try {
                if (iBookInterface == null) {
                    Toast.makeText(it.context, "未获取到binder对象", Toast.LENGTH_SHORT).show()
                    return@setOnClickListener
                }

                if (isInSendFile.get()) {
                    return@setOnClickListener
                }

                CLogUtil.d("send file!")

                isInSendFile.compareAndSet(false, true)

                lifecycleScope.launch {
                    withContext(Dispatchers.IO) {
                        val file = getFdWithAssets("a.txt")
                        val timeoutJob = launch(Dispatchers.Default) {
                            delay(20_000L)
                            isInSendFile.compareAndSet(true, false)
                            coroutineContext.job.cancelChildren()
                        }
                        file?.writeText("a ${System.currentTimeMillis()}") ?: return@withContext
                        CLogUtil.d("file is " + file.path)
                        file.let {
                            val createPipe = ParcelFileDescriptor.createPipe()
                            // 0 read 1 write
                            val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(createPipe[1])
                            val inputStream = file.inputStream()
                            file.inputStream().copyTo(outputStream)
                            outputStream.flush()
                            outputStream.close()
                            inputStream.close()
                            val bundle = Bundle()
                            bundle.putParcelable("fileFd", createPipe[0])
                            CLogUtil.d("## put fileFd")
                            try {
                                CLogUtil.d("## put sendFile")
                                iBookInterface?.sendfile(bundle, object : IAidlCallback.Stub() {
                                    override fun onExecComplete() {
                                        timeoutJob.cancel()
                                        CLogUtil.e("## onExecComplete")
                                        isInSendFile.compareAndSet(true, false)
                                    }
                                })
                                CLogUtil.d("## put sendfile after")
                            } catch (ex: DeadObjectException) {
                                ex.printStackTrace()
                            }
                        }
                    }
                }
            } catch (ex: FileNotFoundException) {
                ex.printStackTrace()
            }
        }

        viewBinding.reBindService.setOnClickListener {
            if (iBookInterface != null) {
                return@setOnClickListener
            }
            connectRemoteService()
        }
    }

    private fun getFdWithAssets(name: String): File? {
        try {
            val inputStream = assets.open(name)
            val reader = inputStream.buffered()
            val buffer = ByteArray(1024)
            val file = File(cacheDir.absolutePath, name)
            file.createNewFile()
            val fileOut = file.outputStream().buffered()
            while (true) {
                val len = reader.read(buffer)
                if (len != -1) {
                    fileOut.write(buffer, 0, len)
                } else {
                    break;
                }
            }
            fileOut.flush()
            fileOut.close()
            return file
        } catch (ex: IOException) {
            ex.printStackTrace()
        }
        return null;
    }

    private fun getRemoteServiceIntent(): Intent {
        val intent = Intent(Intent.ACTION_VIEW)
        intent.action = "aidlserver.app.BookService"
        intent.setPackage("aidlserver.app")
        intent.addCategory(Intent.CATEGORY_DEFAULT)
        return intent
    }

    override fun onDestroy() {
        super.onDestroy()
        try {
            serviceConnection?.let { unbindService(it) }
        } catch (ex: Throwable) {
            ex.printStackTrace()
        }
    }
}